// Package feed impliments the syndication feeds for the browser.
package feed

import (
	"fmt"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"code.google.com/awwwe-browser/auralvm/conversation"
	"code.google.com/awwwe-browser/auralvm/param"
	"code.google.com/awwwe-browser/auralvm/setting"
	"code.google.com/awwwe-browser/auralvm/voisfile"
	"code.google.com/awwwe-browser/data"
	"code.google.com/awwwe-browser/vois"
)

// Alternative HTTP handler for sending data back to the view.
var NextContinuer func(http.ResponseWriter, *http.Request, string)
// Final alternative HTTP handler for sending data back to the view.
var StopContinuer func(http.ResponseWriter, *http.Request, string)

const (
	atomTimeLayout string = "2006-01-02T15:04:05.000-07:00"
	rssTimeLayout  string = time.RFC1123Z
)

// HandlerFeedListTools handles an http request to list the feeds for the view.
func HandlerFeedListTools(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	ContinuerFeedListTools(w, r, data.EmptyString, -1, true)
}

// ContinuerFeedListTools continues with handling an http request to list the feeds.
func ContinuerFeedListTools(w http.ResponseWriter, r *http.Request, intro string, startoptionsat int, continueon bool) {
	var err error
	var conv *conversation.Conversation
	var option conversation.APIOption
	var jsonbb []byte
	conv = conversation.NewConversation()
	conv.Type = conversation.ConversationTypeOptions
	conv.Intro = intro
	conv.InitialIndex = startoptionsat
	conv.Header.Append("These are the syndication feed tools.")
	if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
		// a lot of help or a little help.
		// add helpful
		conv.Helpful = " I will iterate through the names of each of the tools. After i say the name of a tool, say yes, if you want to use that tool."
	}
	if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
		// a lot of help
		// add more helpful
		conv.MoreHelpful = " You may say, yes, back, restart, finish, help, pause, or quit."
	}
	conv.Footer = "That is the end of the list of syndication feed tools."
	conv.CallHelp.Package = param.Package
	conv.CallHelp.Object = "browser"
	conv.CallHelp.Method = "listtools"
	// listen tool
	option = conversation.BuildAPIOption()
	option.VXMLs.Append("Open a syndication feed in a new tab.")
	option.CallYes.Package = param.Package
	option.CallYes.Object = "feed"
	option.CallYes.Method = "listtolisten"
	option.CallYes.Arguments[param.ParamSourceObject] = "feed"
	option.CallYes.Arguments[param.ParamSourceMethod] = "listtools"
	option.CallYes.Arguments[param.ParamSourceOptionIndex] = "1"
	conv.Options = append(conv.Options, option)
	// remove tool
	option = conversation.BuildAPIOption()
	option.VXMLs.Append("Remove a feed.")
	option.CallYes.Package = param.Package
	option.CallYes.Object = "feed"
	option.CallYes.Method = "listtoremove"
	option.CallYes.Arguments[param.ParamSourceObject] = "browser"
	option.CallYes.Arguments[param.ParamSourceMethod] = "listtools"
	option.CallYes.Arguments[param.ParamSourceOptionIndex] = "0"
	conv.Options = append(conv.Options, option)
	// serialize the conversation
	jsonbb, err = conv.Marshal()
	if err == nil {
		w.Write(jsonbb)
	} else {
		if continueon {
			NextContinuer(w, r, intro)
		} else {
			// internal server error
			StopContinuer(w, r, intro)
		}
	}
}

// HandlerFeedListToListen handles an http request to list the feeds so the user can open one in a tab for the view.
func HandlerFeedListToListen(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	ContinuerFeedListToListen(w, r, data.EmptyString, -1, true)
}

// ContinuerFeedListToListen continues listing of feeds for listening.
func ContinuerFeedListToListen(w http.ResponseWriter, r *http.Request, intro string, startoptionsat int, continueon bool) {
	var err error
	var conv *conversation.Conversation
	var option conversation.APIOption
	var feed *Feed
	var feeds SliceFeed
	var i, length, last int
	var jsonbb []byte
	feeds = UnMarshalFeeds()
	if feeds.Len() == 0 {
		intro = "You do not have any syndication feeds yet."
		if continueon {
			NextContinuer(w, r, intro)
		} else {
			// internal server error
			StopContinuer(w, r, intro)
		}
		return
	}
	conv = conversation.NewConversation()
	conv.Type = conversation.ConversationTypeOptions
	conv.Intro = intro
	conv.InitialIndex = startoptionsat
	conv.Header.Append("This tool lets you open a web site's syndication feed in a new tab. ")
	if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
		// a lot of help or a little help.
		// add helpful
		conv.Helpful = "I will iterate through the names of web sites which you are subscribed to. After i say the name of a web site, say yes, if you want to open that web site's syndication feed and listen to it."
	}
	if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
		// a lot of help
		// add more helpful
		conv.MoreHelpful = " You may say, yes, back, restart, finish, help, pause, or quit."
	}
	conv.Footer = "That is the end of the list of web site syndication feeds."
	conv.CallHelp.Package = param.Package
	conv.CallHelp.Object = "feed"
	conv.CallHelp.Method = "listtools"
	conv.InitialIndex = 0
	length = feeds.Len()
	last = length - 1
	for i = 0; i < length; i++ {
		feed = feeds.AtIndex(i)
		option = conversation.BuildAPIOption()
		option.VXMLs.Append(feed.SiteName)
		option.CallYes.Package = param.Package
		option.CallYes.Object = "tab"
		option.CallYes.Method = "opennew"
		option.CallYes.Arguments[param.ParamTabURL] = feed.URL
		if i < last {
			// if a tab is not able to open then the vm should continue with listing feeds to open.
			option.CallYes.Arguments[param.ParamSourceObject] = "feed"
			option.CallYes.Arguments[param.ParamSourceMethod] = "listtoopen"
			option.CallYes.Arguments[param.ParamSourceOptionIndex] = fmt.Sprintf("%d", i)
		} else {
			// if a tab is not able to open then the vm should continue with listing the list of feed tools.
			option.CallYes.Arguments[param.ParamSourceObject] = "feed"
			option.CallYes.Arguments[param.ParamSourceMethod] = "listtools"
			option.CallYes.Arguments[param.ParamSourceOptionIndex] = "0"
		}
		conv.Options = append(conv.Options, option)
	}
	// serialize the conversation
	jsonbb, err = conv.Marshal()
	if err == nil {
		w.Write(jsonbb)
	} else {
		if continueon {
			NextContinuer(w, r, intro)
		} else {
			StopContinuer(w, r, intro)
		}
	}
}

// HandlerFeedListToRemove handles an http request to list feeds so the user can remove some for the view.
func HandlerFeedListToRemove(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	ContinuerFeedListToRemove(w, r, data.EmptyString, -1, true)
}

// ContinuerFeedListToRemove continues with the listing of feeds for removal.
func ContinuerFeedListToRemove(w http.ResponseWriter, r *http.Request, intro string, startoptionsat int, continueon bool) {
	var err error
	var conv *conversation.Conversation
	var option conversation.APIOption
	var feed *Feed
	var feeds SliceFeed
	var i, length int
	var jsonbb []byte
	feeds = UnMarshalFeeds()
	if feeds.Len() == 0 {
		intro = "You have not subscribed to any syndication feeds yet."
		if continueon {
			NextContinuer(w, r, intro)
		} else {
			StopContinuer(w, r, intro)
		}
		return
	}
	conv = conversation.NewConversation()
	conv.Type = conversation.ConversationTypeOptions
	conv.Intro = intro
	conv.InitialIndex = startoptionsat
	conv.Header.Append("This tool lets you remove a web site's syndication feed.")
	if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
		// a lot of help or a little help.
		// add helpful
		conv.Helpful = "I will iterate through the names of every web site syndication feed which you are subscribed to. After i say the name of a web site, say yes, if you want to remove that web site's syndication feed."
	}
	if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
		// a lot of help
		// add more helpful
		conv.MoreHelpful = " You may say, yes, back, restart, finish, help, pause, or quit."
	}
	conv.Footer = "That is the end of the list of syndication feeds."
	conv.CallHelp.Package = param.Package
	conv.CallHelp.Object = "feed"
	conv.CallHelp.Method = "listtools"
	conv.InitialIndex = 0
	length = feeds.Len()
	for i = 0; i < length; i++ {
		feed = feeds.AtIndex(i)
		option = conversation.BuildAPIOption()
		option.VXMLs.Append(feed.SiteName)
		option.CallYes.Package = param.Package
		option.CallYes.Object = "feed"
		option.CallYes.Method = "remove"
		option.CallYes.Arguments[param.ParamFeedKey] = fmt.Sprintf("%d", feed.Key)
		if i < length-1 {
			// after removing a tab the vm should continue with listing feeds to remove.
			option.CallYes.Arguments[param.ParamSourceObject] = "feed"
			option.CallYes.Arguments[param.ParamSourceMethod] = "listtoremove"
			option.CallYes.Arguments[param.ParamSourceOptionIndex] = fmt.Sprintf("%d", i)
		} else {
			// after removing a tab the vm should continue with listing the feed tools.
			option.CallYes.Arguments[param.ParamSourceObject] = "feed"
			option.CallYes.Arguments[param.ParamSourceMethod] = "listtools"
			option.CallYes.Arguments[param.ParamSourceOptionIndex] = "0"
		}
		conv.Options = append(conv.Options, option)
	}
	// serialize the conversation
	jsonbb, err = conv.Marshal()
	if err == nil {
		w.Write(jsonbb)
	} else {
		if continueon {
			NextContinuer(w, r, intro)
		} else {
			StopContinuer(w, r, intro)
		}
	}
}

// HandlerFeedAdd handles an http request to add a feed for the view.
func HandlerFeedAdd(w http.ResponseWriter, r *http.Request) {
	var values []string
	var err error
	var feed Feed
	var feeds SliceFeed
	var intro string
	var ok bool
	var fname, fpath string
	var vdata *vois.VOISData
	var location string
	var vfnum map[string]string
	var fdurl *url.URL
	feed = BuildFeed()
	r.ParseForm()
	// get the url
	values, ok = r.Form[param.ParamFeedURL]
	if !ok || len(values) != 1 {
		intro = "I was not able to add the syndication feed because the voice app did not provide the feed you are ell."
		NextContinuer(w, r, intro)
		return
	} else if len(values) == 1 {
		feed.URL = strings.Trim(values[0], data.WhiteSpaceString)
		if len(feed.URL) == 0 {
			intro = "I was not able to add the syndication feed because the voice app did not provide the feed you are ell."
			NextContinuer(w, r, intro)
			return
		}
	}
	// get the site host
	values, ok = r.Form[param.ParamFeedHost]
	if !ok || len(values) != 1 || (len(values) == 1 && len(values[0]) == 0) {
		intro = "I was not able to add the syndication feed because the voice app did not provide the web site host."
		NextContinuer(w, r, intro)
		return
	} else if len(values) == 1 {
		feed.Host = strings.Trim(values[0], data.WhiteSpaceString)
		if len(feed.Host) == 0 {
			intro = "I was not able to add the syndication feed because the voice app did not provide the web site host."
			NextContinuer(w, r, intro)
			return
		}
	}
	// vois data file
	vfnum = voisfile.GetVOISFileNameURLMap()
	ok = false
	for fname, location = range vfnum {
		fdurl, err = url.Parse(location)
		if err == nil && fdurl.Host == feed.Host {
			fname, err = voisfile.URLToVOISFileName(feed.URL)
			if err == nil {
				fpath = path.Join(param.LocalVOISFolderPath, fname)
				vdata, err = voisfile.GetVOISData(fpath)
				ok = true
			}
		}
	}
	// site name
	if !ok {
		intro = "I was not able to add the syndication feed because i was not able to find the voice dot ex em ell file for the web site."
		NextContinuer(w, r, intro)
		return
	}
	feed.SiteName = vdata.SiteName
	// add the feed
	feeds = UnMarshalFeeds()
	feeds.Append(&feed)
	err = feeds.Marshal()
	if err != nil {
		intro = "I was not able to add the syndication feed because i was not able to write your feeds file."
		NextContinuer(w, r, intro)
		return
	}
	// return the conversation data
	// tab start
	intro = "I have successfully added the syndication feed."
	NextContinuer(w, r, intro)
}

// HandlerFeedRemove handles an http request to remove a feed for the view.
func HandlerFeedRemove(w http.ResponseWriter, r *http.Request) {
	var values []string
	var key int
	var err error
	var feeds SliceFeed
	var intro string
	var ok bool
	var i, length int
	r.ParseForm()
	// get the key
	values, ok = r.Form[param.ParamFeedKey]
	if !ok || len(values) != 1 {
		intro = "I was not able to remove the syndication feed because the voice app did not provide the syndication feed key."
		NextContinuer(w, r, intro)
		return
	}
	key, ok = data.StringToInt(values[0])
	if !ok {
		intro = "I was not able to remove the syndication feed because the voice app did not provide a valid syndication feed key."
		NextContinuer(w, r, intro)
		return
	}
	// read the feeds file
	feeds = UnMarshalFeeds()
	length = feeds.Len()
	for i = 0; i < length; i++ {
		if feeds.AtIndex(i).Key == key {
			// found the feed to return.
			feeds.Delete(i)
			err = feeds.Marshal()
			if err == nil {
				intro = "I have successfully removed the syndication feed."
			} else {
				intro = "I was not able to remove the syndication feed because I was not able to save your syndication feeds file."
			}
            NextContinuer(w, r, intro)
			return
		}
	}
	// feed not found.
	intro = "I was not able to remove the syndication feed because the voice app did not provide the correct syndication feed key."
	NextContinuer(w, r, intro)
	return
}
